package com.engine.salary.entity.salaryacct.bo;

import cn.hutool.core.util.NumberUtil;
import com.engine.salary.annotation.SalaryFormulaVar;
import com.engine.salary.common.LocalDateRange;
import com.engine.salary.constant.SalaryFormulaFieldConstant;
import com.engine.salary.constant.SalaryItemConstant;
import com.engine.salary.entity.datacollection.AddUpDeduction;
import com.engine.salary.entity.datacollection.AddUpSituation;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.datacollection.dto.AttendQuoteDataDTO;
import com.engine.salary.entity.datacollection.dto.AttendQuoteDataValueDTO;
import com.engine.salary.entity.datacollection.dto.AttendQuoteFieldListDTO;
import com.engine.salary.entity.datacollection.po.OtherDeductionPO;
import com.engine.salary.entity.salaryacct.po.SalaryAcctEmployeePO;
import com.engine.salary.entity.salaryacct.po.SalaryAcctResultPO;
import com.engine.salary.entity.salaryarchive.dto.SalaryArchiveDataDTO;
import com.engine.salary.entity.salaryarchive.dto.SalaryArchiveItemDataDTO;
import com.engine.salary.entity.salaryarchive.dto.SalaryArchiveTaxAgentDataDTO;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.entity.salarysob.dto.SalarySobCycleDTO;
import com.engine.salary.entity.salarysob.po.SalarySobAdjustRulePO;
import com.engine.salary.enums.UserStatusEnum;
import com.engine.salary.enums.salaryformula.SalaryFormulaReferenceEnum;
import com.engine.salary.enums.salaryformula.SalarySQLReferenceEnum;
import com.engine.salary.enums.salarysob.SalarySobAdjustRuleTypeEnum;
import com.engine.salary.util.JsonUtil;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import weaver.general.Util;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static com.engine.salary.util.SalaryDateUtil.DATE_TIME_FORMATTER_PATTERN;

/**
 * 薪资核算-将数据转换成公式中的变量
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
@Data
@Accessors(chain = true)
public class CalculateFormulaVarBO {

    /**
     * 员工信息
     */
    private List<DataCollectionEmployee> simpleEmployees;

    /**
     * 薪资档案
     */
    private List<SalaryArchiveDataDTO> salaryArchiveData;

    /**
     * 累计情况
     */
    private List<AddUpSituation> addUpSituationPOS;

    /**
     * 累计专项附加扣除
     */
    private List<AddUpDeduction> addUpDeductionPOS;

    /**
     * 其他扣除
     */
    private List<OtherDeductionPO> otherDeductionPOS;

    /**
     * 社保福利
     */
    private List<Map<String, Object>> welfareData;

    /**
     * 考勤数据
     */
    private List<AttendQuoteDataDTO> attendQuoteDataDTOS;

    /**
     * 薪资核算结果（输入/导入的值）
     */
    private List<SalaryAcctResultPO> salaryAcctResultPOS;

    /**
     * 浮动薪资档案
     */
    private List<Map<String, Object>> variableArchiveList;

    /**
     * 上月核算结果
     */
    List<SalaryAcctResultPO> lastMonthResultPOS;

    public CalculateFormulaVarBO(List<DataCollectionEmployee> simpleEmployees,
                                 List<SalaryArchiveDataDTO> salaryArchiveData,
                                 List<AddUpSituation> addUpSituationPOS,
                                 List<AddUpDeduction> addUpDeductionPOS,
                                 List<OtherDeductionPO> otherDeductionPOS,
                                 List<Map<String, Object>> welfareData,
                                 List<AttendQuoteDataDTO> attendQuoteDataDTOS,
                                 List<SalaryAcctResultPO> salaryAcctResultPOS,
                                 List<Map<String, Object>> variableArchiveList,
                                 List<SalaryAcctResultPO> lastMonthResultPOS) {
        this.simpleEmployees = simpleEmployees;
        this.salaryArchiveData = salaryArchiveData;
        this.addUpSituationPOS = addUpSituationPOS;
        this.addUpDeductionPOS = addUpDeductionPOS;
        this.otherDeductionPOS = otherDeductionPOS;
        this.welfareData = welfareData;
        this.attendQuoteDataDTOS = attendQuoteDataDTOS;
        this.salaryAcctResultPOS = salaryAcctResultPOS;
        this.variableArchiveList = variableArchiveList;
        this.lastMonthResultPOS = lastMonthResultPOS;
    }

    /**
     * 将查询到的数据转换成公式变量
     *
     * @param salaryAcctCalculateBO 核算参数
     * @return
     */
    public Map<String, List<FormulaVarValue>> convert2FormulaVar(SalaryAcctCalculateBO salaryAcctCalculateBO) {
        Map<String, List<FormulaVarValue>> resultMap = Maps.newHashMapWithExpectedSize(salaryAcctCalculateBO.getSalaryAcctEmployeePOS().size());
        // 处理薪资核算结果
        handleSalaryAcctResult(salaryAcctCalculateBO, resultMap);
        // 处理薪资档案
        handleSalaryArchiveData(salaryAcctCalculateBO, resultMap);
        // 处理浮动薪资档案
        handleVariableArchiveData(salaryAcctCalculateBO, resultMap);
        // 处理往期累计情况
        handleAddUpSituation(resultMap);
        // 处理累计专项附加扣除
        handleAddUpDeduction(resultMap);
        // 处理其他扣除
        handleOtherDeduction(resultMap);
        // 处理社保福利
        handleWelfareData(salaryAcctCalculateBO, resultMap);
        // 处理考勤数据
        handleAttendQuoteData(salaryAcctCalculateBO, resultMap);
        //处理核算人员信息
        handleSalaryAcctEmployee(salaryAcctCalculateBO, resultMap);
        //处理人员信息
        handleSimpleEmployees(resultMap);
        //处理上月薪资核算结果
        handleLastMonthResult(salaryAcctCalculateBO, resultMap);
        return resultMap;
    }

    /**
     * 处理核算日期相关信息
     */
    private List<FormulaVarValue> handleSalarySobCycleDTO(SalaryAcctCalculateBO salaryAcctCalculateBO) {

        SalarySobCycleDTO salarySobCycleDTO = salaryAcctCalculateBO.getSalarySobCycleDTO();

        salarySobCycleDTO.setSalaryDate(SalaryDateUtil.toDate(salarySobCycleDTO.getSalaryMonth(), 1));
        salarySobCycleDTO.setTaxDate(SalaryDateUtil.toDate(salarySobCycleDTO.getTaxCycle(), 1));
        salarySobCycleDTO.setSocialSecurityDate(SalaryDateUtil.toDate(salarySobCycleDTO.getSocialSecurityCycle(), 1));
        LocalDateRange salaryCycle = salarySobCycleDTO.getSalaryCycle();
        salarySobCycleDTO.setSalaryCycleFromDate(salaryCycle.getFromDate());
        salarySobCycleDTO.setSalaryCycleEndDate(salaryCycle.getEndDate());
        LocalDateRange attendCycle = salarySobCycleDTO.getAttendCycle();
        salarySobCycleDTO.setAttendCycleFromDate(attendCycle.getFromDate());
        salarySobCycleDTO.setAttendCycleEndDate(attendCycle.getEndDate());

        Map<String, String> map = JsonUtil.parseMap(salarySobCycleDTO, String.class);

        List<FormulaVarValue> formulaVarValues = Lists.newArrayList();
        Field[] declaredFields = SalarySobCycleDTO.class.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(SalaryFormulaVar.class)) {
                String fieldName = declaredField.getName();
                String fieldId = SalarySQLReferenceEnum.SALARY_CYCLE.getValue()
                        + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                        + fieldName;
                String fieldValue = map.getOrDefault(fieldName, StringUtils.EMPTY);
                //日期值取yyyy-MM-dd
                if (StringUtils.isNotBlank(fieldValue) && fieldValue.length() > 10 && SalaryDateUtil.parse(fieldValue, DATE_TIME_FORMATTER_PATTERN) != null) {
                    fieldValue = fieldValue.substring(0, 10);
                }
                formulaVarValues.add(new FormulaVarValue().setFieldId(fieldId).setFieldValue(fieldValue));
            }
        }
        return formulaVarValues;
    }


    /**
     * 处理薪资核算结果
     *
     * @param salaryAcctCalculateBO 薪资核算参数
     * @param resultMap             返回结果集
     */
    private void handleSalaryAcctResult(SalaryAcctCalculateBO salaryAcctCalculateBO, Map<String, List<FormulaVarValue>> resultMap) {
        // key:薪资项目的id、value:薪资项目的code
        Map<Long, String> salaryItemCodeMap = SalaryEntityUtil.convert2Map(salaryAcctCalculateBO.getSalaryItemPOS(), SalaryItemPO::getId, SalaryItemPO::getCode);
        // key:薪资项目的code、value:薪资项目的id
        Map<String, Long> salaryCodeItemMap = SalaryEntityUtil.convert2Map(salaryAcctCalculateBO.getSalaryItemPOS(), SalaryItemPO::getCode, SalaryItemPO::getId);
        // key:employeeId_taxAgentId、value:薪资核算结果集合
        Map<String, List<SalaryAcctResultPO>> salaryAcctResultMap = SalaryEntityUtil.group2Map(salaryAcctResultPOS,
                salaryAcctResultPO -> salaryAcctResultPO.getEmployeeId() + "_" + salaryAcctResultPO.getTaxAgentId());
        //
//        List<ExpressFormula> expressFormulas = salaryAcctCalculateBO.getExpressFormulas();
        // 填充到返回结果集中
        salaryAcctResultMap.forEach((key, salaryAcctResultPOS) -> {
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(key, k -> Lists.newArrayList());
            formulaVarValues.addAll(salaryAcctResultPOS.stream()
                    .map(salaryAcctResultPO -> {
                        String fieldId = SalaryFormulaReferenceEnum.SALARY_ITEM.getValue()
                                + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                                + salaryItemCodeMap.getOrDefault(salaryAcctResultPO.getSalaryItemId(), StringUtils.EMPTY);
                        return new FormulaVarValue().setFieldId(fieldId).setFieldValue(salaryAcctResultPO.getResultValue());
                    })
                    .collect(Collectors.toList()));
            Map<Long, SalaryAcctResultPO> salaryAcctResultPOMap = SalaryEntityUtil.convert2Map(salaryAcctResultPOS, SalaryAcctResultPO::getSalaryItemId);
            formulaVarValues.addAll(salaryAcctCalculateBO.getIssuedFieldIds().stream()
                    .map(fieldId -> {
                        String originResultValue = salaryAcctResultPOMap.getOrDefault(
                                        salaryCodeItemMap.get(fieldId), SalaryAcctResultPO.builder().originResultValue("").build())
                                .getOriginResultValue();
                        String fieldId2 = SalaryFormulaReferenceEnum.ISSUED.getValue()
                                + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                                + fieldId;
                        return new FormulaVarValue().setFieldId(fieldId2).setFieldValue(originResultValue);
                    }).collect(Collectors.toList()));
        });
    }

    private void handleLastMonthResult(SalaryAcctCalculateBO salaryAcctCalculateBO, Map<String, List<FormulaVarValue>> resultMap) {
        // key:薪资项目的id、value:薪资项目的code
        Map<Long, String> salaryItemCodeMap = SalaryEntityUtil.convert2Map(salaryAcctCalculateBO.getSalaryItemPOS(), SalaryItemPO::getId, SalaryItemPO::getCode);
        // key:employeeId_taxAgentId、value:薪资核算结果集合
        Map<String, List<SalaryAcctResultPO>> salaryAcctResultMap = SalaryEntityUtil.group2Map(lastMonthResultPOS,
                salaryAcctResultPO -> salaryAcctResultPO.getEmployeeId() + "_" + salaryAcctResultPO.getTaxAgentId());
        // 填充到返回结果集中
        salaryAcctResultMap.forEach((key, salaryAcctResultPOS) -> {
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(key, k -> Lists.newArrayList());
            formulaVarValues.addAll(salaryAcctResultPOS.stream()
                    .map(salaryAcctResultPO -> {
                        String fieldId = SalaryFormulaReferenceEnum.LAST_MONTH_CALC.getValue()
                                + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                                + salaryItemCodeMap.getOrDefault(salaryAcctResultPO.getSalaryItemId(), StringUtils.EMPTY);
                        return new FormulaVarValue().setFieldId(fieldId).setFieldValue(salaryAcctResultPO.getResultValue());
                    })
                    .collect(Collectors.toList()));
        });
    }

    /**
     * 处理薪资档案（会涉及调薪计薪规则）+处理核算日期
     *
     * @param salaryAcctCalculateBO 薪资核算参数
     * @param resultMap             返回结果集
     */
    private void handleSalaryArchiveData(SalaryAcctCalculateBO salaryAcctCalculateBO, Map<String, List<FormulaVarValue>> resultMap) {
        // 调薪计薪规则
        Map<Long, SalarySobAdjustRulePO> salarySobAdjustRulePOMap = SalaryEntityUtil.convert2Map(salaryAcctCalculateBO.getSalarySobAdjustRulePOS(),
                SalarySobAdjustRulePO::getSalaryItemId);
        for (SalaryArchiveDataDTO salaryArchiveDataDTO : salaryArchiveData) {
            for (SalaryArchiveTaxAgentDataDTO salaryArchiveTaxAgentDataDTO : salaryArchiveDataDTO.getTaxAgents()) {
                String key = salaryArchiveDataDTO.getEmployeeId() + "_" + salaryArchiveTaxAgentDataDTO.getTaxAgentId();
                List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(key, k -> Lists.newArrayList());
                // 将薪资档案的值转换成公式中的变量，填充到返回结果集中
                formulaVarValues.addAll(handleSalaryArchiveItemVal(salaryAcctCalculateBO, salaryArchiveTaxAgentDataDTO.getSalaryItemValues(), salarySobAdjustRulePOMap));
            }
        }
    }

    private void handleVariableArchiveData(SalaryAcctCalculateBO salaryAcctCalculateBO, Map<String, List<FormulaVarValue>> resultMap) {
        Map<String, Map<String, Object>> variableArchiveMap = SalaryEntityUtil.convert2Map(variableArchiveList, map -> map.getOrDefault("taxAgentIds", "").toString() + "-" + map.getOrDefault("employeeId", "").toString());
        // 填充到返回结果集中
        for (SalaryAcctEmployeePO salaryAcctEmployeePO : salaryAcctCalculateBO.getSalaryAcctEmployeePOS()) {
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(salaryAcctEmployeePO.getEmployeeId() + "_" + salaryAcctEmployeePO.getTaxAgentId(),
                    k -> Lists.newArrayList());

            String key = salaryAcctEmployeePO.getTaxAgentId().toString() + "-" + salaryAcctEmployeePO.getEmployeeId().toString();
            Map<String, Object> map = variableArchiveMap.getOrDefault(key, Collections.emptyMap());
            formulaVarValues.addAll(salaryAcctCalculateBO.getVariableItems().stream().map(field -> {
                String fieldId = SalaryFormulaReferenceEnum.VARIABLE_ITEM.getValue()
                        + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                        + field.getCode();

                String value = map.getOrDefault(field.getId() + SalaryItemConstant.VARIABLE_ITEM_DYNAMIC_SUFFIX, StringUtils.EMPTY) == null ? StringUtils.EMPTY
                        : map.getOrDefault(field.getId() + SalaryItemConstant.VARIABLE_ITEM_DYNAMIC_SUFFIX, StringUtils.EMPTY).toString();
                return new FormulaVarValue().setFieldId(fieldId).setFieldValue(value);
            }).collect(Collectors.toList()));
        }
    }

    /**
     * 根据调薪计薪规则处理薪资档案的调薪，转换成公式编辑器中的变量
     *
     * @param salaryAcctCalculateBO
     * @param salaryArchiveItemDataList
     * @param salarySobAdjustRulePOMap
     * @return
     */
    private List<FormulaVarValue> handleSalaryArchiveItemVal(SalaryAcctCalculateBO salaryAcctCalculateBO,
                                                             List<SalaryArchiveItemDataDTO> salaryArchiveItemDataList,
                                                             Map<Long, SalarySobAdjustRulePO> salarySobAdjustRulePOMap) {
        // 薪资周期
        LocalDateRange salaryCycle = salaryAcctCalculateBO.getSalarySobCycleDTO().getSalaryCycle();
        // key:薪资项目的id、value:薪资项目的code
        Map<Long, String> salaryItemCodeMap = SalaryEntityUtil.convert2Map(salaryAcctCalculateBO.getSalaryItemPOS(), SalaryItemPO::getId, SalaryItemPO::getCode);
        // 将薪资档案的调薪记录按照薪资项目id聚合(同一个薪资项目可能存在多次调薪，按照生效日期对调薪记录排序)
        Map<Long, List<SalaryArchiveItemDataDTO>> dataMap = salaryArchiveItemDataList.stream()
                .collect(Collectors.groupingBy(SalaryArchiveItemDataDTO::getSalaryItemId,
                        Collectors.collectingAndThen(Collectors.toList(), salaryArchiveItemDataDTOS -> salaryArchiveItemDataDTOS.stream()
                                .sorted(Comparator.comparing(salaryArchiveItemDataDTO -> salaryArchiveItemDataDTO.getEffectiveDateRange().getFromDate()))
                                .collect(Collectors.toList()))));
        // 将薪资档案的值转换成公式编辑器中的变量
        List<FormulaVarValue> formulaVarValues = Lists.newArrayListWithExpectedSize(dataMap.size());
        for (Map.Entry<Long, List<SalaryArchiveItemDataDTO>> entry : dataMap.entrySet()) {
            String value;
            // 获取薪资项目的调薪规则
            SalarySobAdjustRulePO salaryAdjustmentRulePO = salarySobAdjustRulePOMap.get(entry.getKey());
            if (entry.getValue().size() > 2) {
                // 如果薪资项目在薪资周期内经历了多次调薪，则默认分段计薪
                value = calculateBySalarySobAdjustRule(salaryCycle, SalarySobAdjustRuleTypeEnum.PARTITION, entry.getValue());
            } else if (salaryAdjustmentRulePO == null || entry.getValue().size() < 2) {
                // 如果薪资项目没有设置调薪计薪规则，默认取薪资周期内薪资档案中最新的值
                // 如果薪资项目在薪资周期内没有调薪，默认取薪资周期内薪资档案中最新的值
                value = calculateBySalarySobAdjustRule(salaryCycle, SalarySobAdjustRuleTypeEnum.USE_AFTER_ADJUSTMENT, entry.getValue());
            } else {
                // 如果薪资项目在薪资周期内只有一次调薪，则根据调薪计薪规则处理
                SalarySobAdjustRuleTypeEnum adjustRuleTypeEnum = salaryAdjustmentRulePO.getDayOfMonth() < SalaryDateUtil.dateToLocalDate(entry.getValue().get(0).getEffectiveDateRange().getEndDate()).getDayOfMonth()
                        ? SalarySobAdjustRuleTypeEnum.parseByValue(salaryAdjustmentRulePO.getAfterAdjustmentType())
                        : SalarySobAdjustRuleTypeEnum.parseByValue(salaryAdjustmentRulePO.getBeforeAdjustmentType());
                // 根据调薪计薪规则处理薪资档案的调薪
                value = calculateBySalarySobAdjustRule(salaryCycle, adjustRuleTypeEnum, entry.getValue());
            }
            String fieldId = SalaryFormulaReferenceEnum.SALARY_ARCHIVES.getValue()
                    + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                    + salaryItemCodeMap.getOrDefault(entry.getKey(), StringUtils.EMPTY);
            formulaVarValues.add(new FormulaVarValue().setFieldId(fieldId).setFieldValue(value));
        }
        return formulaVarValues;
    }

    /**
     * 根据调薪计薪规则计算调薪后的值
     *
     * @param adjustRuleTypeEnum
     * @param salaryArchiveItemDataDTOS
     * @return
     */
    private String calculateBySalarySobAdjustRule(LocalDateRange salaryCycle,
                                                  SalarySobAdjustRuleTypeEnum adjustRuleTypeEnum,
                                                  List<SalaryArchiveItemDataDTO> salaryArchiveItemDataDTOS) {
        if (Objects.isNull(adjustRuleTypeEnum)) {
            return StringUtils.EMPTY;
        }
        String value;
        switch (adjustRuleTypeEnum) {
            case AVERAGE:
                // 可能存在多次调薪
                // = (第一段的值+第二段的值+……)/n
                value = String.valueOf(salaryArchiveItemDataDTOS.stream()
                        .mapToDouble(salaryArchiveItemDataDTO -> SalaryEntityUtil.empty2Zero(salaryArchiveItemDataDTO.getValue()).doubleValue())
                        .average().orElse(NumberUtils.DOUBLE_ZERO));
                break;
            case PARTITION:
                // 可能存在多次调薪
                // = (第一段的值*第一段的自然日+第二段的值*第二段的自然日+……)/薪资所属月自然日
                BigDecimal valueSum = BigDecimal.ZERO;
                for (int i = 0; i < salaryArchiveItemDataDTOS.size(); i++) {
                    SalaryArchiveItemDataDTO dataDTO = salaryArchiveItemDataDTOS.get(i);
                    BigDecimal dayDiff = new BigDecimal(SalaryDateUtil.dateToLocalDate(dataDTO.getEffectiveDateRange().getFromDate()).until(SalaryDateUtil.dateToLocalDate(dataDTO.getEffectiveDateRange().getEndDate()), ChronoUnit.DAYS));
                    // 最后一段日期范围需要加一
                    if (Objects.equals(i, salaryArchiveItemDataDTOS.size() - 1)) {
                        dayDiff = dayDiff.add(BigDecimal.ONE);
                    }
                    valueSum = valueSum.add(SalaryEntityUtil.empty2Zero(dataDTO.getValue()).multiply(dayDiff));
                }
                value = valueSum.divide(new BigDecimal(SalaryDateUtil.dateToLocalDate(salaryCycle.getFromDate()).until(SalaryDateUtil.dateToLocalDate(salaryCycle.getEndDate()), ChronoUnit.DAYS)).add(BigDecimal.ONE), 2, RoundingMode.HALF_UP).toPlainString();
                break;
            case USE_BEFORE_ADJUSTMENT:
                // = 调薪前工资
                value = salaryArchiveItemDataDTOS.get(0).getValue();
                break;
            case USE_AFTER_ADJUSTMENT:
                // = 调薪后工资
                value = salaryArchiveItemDataDTOS.get(salaryArchiveItemDataDTOS.size() - 1).getValue();
                break;
            default:
                value = "0";
                break;
        }
        return value;
    }

    /**
     * 处理累计情况（工资、薪金）
     *
     * @param resultMap 返回结果集
     */
    private void handleAddUpSituation(Map<String, List<FormulaVarValue>> resultMap) {
        // key:employeeId_taxAgentId、value:累计情况（工资、薪金）的数据
        Map<String, AddUpSituation> addUpSituationPOMap = SalaryEntityUtil.convert2Map(addUpSituationPOS,
                addUpSituationPO -> addUpSituationPO.getEmployeeId() + "_" + addUpSituationPO.getTaxAgentId());
        // 累计情况（工资、薪金）可选字段
        List<String> fieldNames = Lists.newArrayList();
        Field[] declaredFields = AddUpSituation.class.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(SalaryFormulaVar.class)) {
                fieldNames.add(declaredField.getName());
            }
        }
        // 填充到返回结果集中
        addUpSituationPOMap.forEach((key, addUpSituationPO) -> {
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(key, k -> Lists.newArrayList());
            Map<String, String> map = JsonUtil.parseMap(addUpSituationPO, String.class);
            formulaVarValues.addAll(fieldNames.stream().map(fieldName -> {
                String fieldId = SalaryFormulaReferenceEnum.ADD_UP_SITUATION.getValue()
                        + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                        + fieldName;
                return new FormulaVarValue().setFieldId(fieldId).setFieldValue(map.getOrDefault(fieldName, StringUtils.EMPTY));
            }).collect(Collectors.toList()));
        });
    }

    /**
     * 处理累计专项附加扣除
     *
     * @param resultMap 返回结果集
     */
    private void handleAddUpDeduction(Map<String, List<FormulaVarValue>> resultMap) {
        // key:employeeId_taxAgentId、value:累计专项附加扣除的数据
        Map<String, AddUpDeduction> addUpDeductionPOMap = SalaryEntityUtil.convert2Map(addUpDeductionPOS,
                addUpDeductionPO -> addUpDeductionPO.getEmployeeId() + "_" + addUpDeductionPO.getTaxAgentId());
        // 累计专项附加扣除可选字段
        List<String> fieldNames = Lists.newArrayList();
        Field[] declaredFields = AddUpDeduction.class.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(SalaryFormulaVar.class)) {
                fieldNames.add(declaredField.getName());
            }
        }
        // 填充到返回结果集中
        addUpDeductionPOMap.forEach((key, addUpDeductionPO) -> {
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(key, k -> Lists.newArrayList());
            Map<String, String> map = JsonUtil.parseMap(addUpDeductionPO, String.class);
            formulaVarValues.addAll(fieldNames.stream().map(fieldName -> {
                String fieldId = SalaryFormulaReferenceEnum.ADD_UP_DEDUCTIONS.getValue()
                        + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                        + fieldName;
                return new FormulaVarValue().setFieldId(fieldId).setFieldValue(map.getOrDefault(fieldName, StringUtils.EMPTY));
            }).collect(Collectors.toList()));
        });
    }

    /**
     * 处理其他扣除
     *
     * @param resultMap 返回结果集
     */
    private void handleOtherDeduction(Map<String, List<FormulaVarValue>> resultMap) {
        // key:employeeId_taxAgentId、value:累计专项附加扣除的数据
        Map<String, OtherDeductionPO> otherDeductionPOMap = SalaryEntityUtil.convert2Map(otherDeductionPOS,
                otherDeductionPO -> otherDeductionPO.getEmployeeId() + "_" + otherDeductionPO.getTaxAgentId());
        // 累计专项附加扣除可选字段
        List<String> fieldNames = Lists.newArrayList();
        Field[] declaredFields = OtherDeductionPO.class.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(SalaryFormulaVar.class)) {
                fieldNames.add(declaredField.getName());
            }
        }
        // 填充到返回结果集中
        otherDeductionPOMap.forEach((key, otherDeductionPO) -> {
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(key, k -> Lists.newArrayList());
            Map<String, String> map = JsonUtil.parseMap(otherDeductionPO, String.class);
            formulaVarValues.addAll(fieldNames.stream().map(fieldName -> {
                String fieldId = SalaryFormulaReferenceEnum.OTHER_DEDUCTION.getValue()
                        + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                        + fieldName;
                return new FormulaVarValue().setFieldId(fieldId).setFieldValue(map.getOrDefault(fieldName, StringUtils.EMPTY));
            }).collect(Collectors.toList()));
        });
    }

    /**
     * 处理社保福利数据
     *
     * @param resultMap 返回结果集
     */
    private void handleWelfareData(SalaryAcctCalculateBO salaryAcctCalculateBO, Map<String, List<FormulaVarValue>> resultMap) {
        // 社保福利可选字段
        List<String> fieldNames = Lists.newArrayList(salaryAcctCalculateBO.getWelfareColumns().values());
        // 社保福利数据
        Map<String, List<FormulaVarValue>> tempMap = new HashMap<>();
        welfareData.forEach(map -> {
            String key = map.getOrDefault("employeeId", StringUtils.EMPTY) + "_" + map.getOrDefault("taxAgentId", StringUtils.EMPTY);
            List<FormulaVarValue> formulaVarValues = tempMap.computeIfAbsent(key, k -> Lists.newArrayList());
            formulaVarValues.addAll(fieldNames.stream().map(fieldName -> {
                String fieldId = SalaryFormulaReferenceEnum.WELFARE.getValue()
                        + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                        + fieldName;
                return new FormulaVarValue().setFieldId(fieldId).setFieldValue(String.valueOf(map.getOrDefault(fieldName, StringUtils.EMPTY)));
            }).collect(Collectors.toList()));
        });
        // 填充到返回结果集中
        for (SalaryAcctEmployeePO salaryAcctEmployeePO : salaryAcctCalculateBO.getSalaryAcctEmployeePOS()) {
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(salaryAcctEmployeePO.getEmployeeId() + "_" + salaryAcctEmployeePO.getTaxAgentId(),
                    k -> Lists.newArrayList());
            formulaVarValues.addAll(tempMap.getOrDefault(salaryAcctEmployeePO.getEmployeeId() + "_" + salaryAcctEmployeePO.getTaxAgentId(), Collections.emptyList()));
        }
    }

    /**
     * 处理考勤引用数据
     *
     * @param salaryAcctCalculateBO
     * @param resultMap
     */
    private void handleAttendQuoteData(SalaryAcctCalculateBO salaryAcctCalculateBO, Map<String, List<FormulaVarValue>> resultMap) {
        // 考勤引用可选字段
        Set<Long> fieldNames = SalaryEntityUtil.properties(salaryAcctCalculateBO.getAttendQuoteFieldListDTOS(), AttendQuoteFieldListDTO::getId);
        // 考勤引用数据
        Map<Long, List<FormulaVarValue>> tempMap = Maps.newHashMapWithExpectedSize(attendQuoteDataDTOS.size());
        attendQuoteDataDTOS.forEach(attendQuoteDataDTO -> {
            Map<Long, String> attendQuoteDataValueMap = SalaryEntityUtil.convert2Map(attendQuoteDataDTO.getDataValues(), AttendQuoteDataValueDTO::getAttendQuoteFieldId, AttendQuoteDataValueDTO::getDataValue);
            Long key = attendQuoteDataDTO.getEmployeeId();
            List<FormulaVarValue> formulaVarValues = tempMap.computeIfAbsent(key, k -> Lists.newArrayList());
            formulaVarValues.addAll(fieldNames.stream().map(fieldName -> {
                String fieldId = SalaryFormulaReferenceEnum.ATTEND.getValue()
                        + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                        + fieldName;
                return new FormulaVarValue().setFieldId(fieldId).setFieldValue(attendQuoteDataValueMap.getOrDefault(fieldName, StringUtils.EMPTY));
            }).collect(Collectors.toList()));
        });
        // 填充到返回结果集中
        for (SalaryAcctEmployeePO salaryAcctEmployeePO : salaryAcctCalculateBO.getSalaryAcctEmployeePOS()) {
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(salaryAcctEmployeePO.getEmployeeId() + "_" + salaryAcctEmployeePO.getTaxAgentId(),
                    k -> Lists.newArrayList());
            formulaVarValues.addAll(tempMap.getOrDefault(salaryAcctEmployeePO.getEmployeeId(), Collections.emptyList()));
        }
    }

    /**
     * 处理核算人员信息
     *
     * @param salaryAcctCalculateBO
     * @param resultMap
     */
    private void handleSalaryAcctEmployee(SalaryAcctCalculateBO salaryAcctCalculateBO, Map<String, List<FormulaVarValue>> resultMap) {
        List<SalaryAcctEmployeePO> salaryAcctEmployeePOS = salaryAcctCalculateBO.getSalaryAcctEmployeePOS();

        // key:employeeId_taxAgentId、value:核算人员信息
        Map<String, SalaryAcctEmployeePO> salaryAcctEmployeeMap = SalaryEntityUtil.convert2Map(salaryAcctEmployeePOS,
                po -> po.getEmployeeId() + "_" + po.getTaxAgentId());
        // 核算人员信息可选字段
        List<String> fieldNames = Lists.newArrayList();
        Field[] declaredFields = SalaryAcctEmployeePO.class.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(SalaryFormulaVar.class)) {
                fieldNames.add(declaredField.getName());
            }
        }
        // 填充到返回结果集中
        salaryAcctEmployeeMap.forEach((key, po) -> {
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(key, k -> Lists.newArrayList());
            Map<String, String> map = JsonUtil.parseMap(po, String.class);
            formulaVarValues.addAll(fieldNames.stream().map(fieldName -> {
                String fieldId = SalarySQLReferenceEnum.SALARY_ACCT_EMPLOYEE.getValue()
                        + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                        + fieldName;
                String fieldValue = map.getOrDefault(fieldName, StringUtils.EMPTY);
                //判断是否是日期，日期值取yyyy-MM-dd
                if (StringUtils.isNotBlank(fieldValue) && fieldValue.length() > 10 && SalaryDateUtil.parse(fieldValue, DATE_TIME_FORMATTER_PATTERN) != null) {
                    fieldValue = fieldValue.substring(0, 10);
                }
                return new FormulaVarValue().setFieldId(fieldId).setFieldValue(fieldValue);
            }).collect(Collectors.toList()));

            //将薪资周期转换成公式中的变量，填充到返回结果集中
            formulaVarValues.addAll(handleSalarySobCycleDTO(salaryAcctCalculateBO));
        });

    }

    /**
     * 处理员工基本信息
     *
     * @param resultMap
     */
    private void handleSimpleEmployees(Map<String, List<FormulaVarValue>> resultMap) {

        // key:employeeId_taxAgentId、value:员工信息
        Map<String, DataCollectionEmployee> employeeMap = SalaryEntityUtil.convert2Map(simpleEmployees,
                po -> po.getEmployeeId() + "");
        // 核算人员信息可选字段
        List<String> fieldNames = Lists.newArrayList();
        Field[] declaredFields = DataCollectionEmployee.class.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(SalaryFormulaVar.class)) {
                fieldNames.add(declaredField.getName());
            }
        }
        // 填充到返回结果集中
        employeeMap.forEach((key, po) -> {
            // 获取po的状态
            if (po.getStatus() != null && NumberUtil.isNumber(po.getStatus())) {
                po.setStatusName(UserStatusEnum.getDefaultLabelByValue(new Integer(Util.null2s(po.getStatus(), "1"))));
            }
            List<FormulaVarValue> formulaVarValues = resultMap.computeIfAbsent(key, k -> Lists.newArrayList());
            Map<String, String> map = JsonUtil.parseMap(po, String.class);
            formulaVarValues.addAll(fieldNames.stream().map(fieldName -> {
                String fieldId = SalarySQLReferenceEnum.EMPLOYEE_INFO.getValue()
                        + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR
                        + fieldName;
                return new FormulaVarValue().setFieldId(fieldId).setFieldValue(map.getOrDefault(fieldName, StringUtils.EMPTY));
            }).collect(Collectors.toList()));
        });

    }


    @Data
    @Accessors(chain = true)
    public static class FormulaVarValue {

        /**
         * 公式变量id
         */
        private String fieldId;

        /**
         * 公式变量的值
         */
        private String fieldValue;
    }
}
